OData publishing, external-entity imports, dynamic SQL and folders: nine formula1 findings - #114
Merged
Merged
Conversation
`mxcli test tests/ -p app/App.mpr` ran from the solution root and
`--list` on the same command line did not:
Error: stat tests/: no such file or directory
resolveTestPaths was called below the --list branch, which returned first.
Listing and running now accept the same paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Every whole-number attribute in a published service failed the build, one
CE5016 each:
Attribute …Stg_Season.Year is has type Integer, but is published as
Edm.Int32.
Mendix publishes Integer as Int64, same as Long. The mapping's own comment
flagged Integer as an unverified guess, and the existing test pinned the guess.
Publishing every attribute type on 11.12.1 and reading the errors off the build
also caught a second wrong pair the report had only suspected: an enumeration
was written as Edm.String with EnumerationAsString hardcoded false, which is the
one combination Mendix rejects — CE5016 plus CE4583 "Enumeration 'Edm.Colour' is
not published in this service". The type and the flag are one setting, so the
flag now travels with the attribute.
Verified: the same all-types service builds 0 errors.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
`create or modify external entity` touching only an entity-level property
detonated every attribute of the entity:
[CE6612] "Attribute 'circuitId' of external entity 'Stg_Circuit' is not supported."
one per attribute, leaving a project that cannot build.
The executor already preserves attributes it was not asked to change, so the
loss was a layer down: attributeFromGen handled StoredValue and OqlViewValue but
not Rest$ODataMappedValue. Every attribute of an external entity therefore came
back with no RemoteName, and the writer's `isExternal && a.RemoteName != ""` arm
fell through to a plain StoredValue on the next read-modify-write. The
per-attribute Filterable/Sortable/Creatable/Updatable flags live on the same
value and were lost with it.
This is the attribute-level half of mendixlabs#782, which fixed the entity level only.
Verified on 11.12.1 against a real contract import: three CE6612 before, none
after, and the modify's own change still lands.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
CREATE EXTERNAL ENTITIES read names, types and navigation properties out of the
contract correctly, then defaulted every capability to true regardless of what
the contract said. Mendix compares the two at build time and refuses:
'Seasons' is marked Countable=False in the OData service, but True in the app.
'latitude' is marked Filterable=False in the OData service, but True in the app.
Eight errors from an eight-resource import — on the one command whose whole job
is fidelity to the contract.
Insert/Update/Delete restrictions were already parsed; Count/Filter/Sort were
not, so there was nothing for the import to honour. An unannotated set still
means countable/filterable/sortable, which is OData's own default — silence is
not a restriction.
Verified on 11.12.1: a contract declaring CountRestrictions/Countable=false and
NonFilterableProperties produced two CE6630 before, none after.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
CREATE ODATA CLIENT accepts UseAuthentication / HttpUsername / HttpPassword and stores them for the runtime, but the design-time fetch was a bare client.Get. Against a service behind `authentication basic` that is a 401 — and since the failure is only a warning, the client is created with no cached entity types, so the CREATE EXTERNAL ENTITIES that follows imports nothing from a script that looks like it succeeded. The credentials and any HEADERS now go out with the fetch. Only literals can be used. The visitor strips a quoted literal's quotes, so 'f1api' and Module.ApiUser both arrive as bare strings; the AST now records which was written. A constant is resolved by the runtime, and sending its *name* as the password would be worse than sending nothing — so unresolved names are reported instead, alongside a note that the client was left empty and that pointing MetadataUrl at a committed contract file avoids the problem entirely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
`execute database query … dynamic $Sql` reached the runtime as the string
literal '$Sql', so the database was asked to execute four characters:
ERROR - ExternalDatabaseConnector: Parser Error: syntax error at or near "$"
The builder quoted anything not already starting with a quote — correct for
`dynamic 'SELECT …'`, wrong for an expression — and the AST kept no
literal-vs-expression flag, so it could not tell them apart. That blocked
runtime-built SQL, and therefore query pushdown, outright.
Verified by reading the stored BSON: DynamicQuery now holds $Sql, not '$Sql'.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
The hint said "Known properties here: ReadMode, InsertMode, UpdateMode, DeleteMode, UsePaging, PageSize" long after the visitor learned Countable, SkipSupported and TopSupported — so a user typing an accepted property was told it was unknown. The lists are separate by design (the visitor decides, the hint displays), but nothing kept them in step. The AST struct is now the source: every field of PublishedEntityDef and CreateExternalEntityStmt must be advertised or explicitly listed as structural, so adding a property and forgetting the hint is a test failure instead of a wrong message. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
`mxcli test --local` sets the after-startup microflow to its own endpoint
registrar and restores it afterwards — deliberate, because a test run wants a
known starting state. But it said only:
After-startup set to MxTest.RegisterEndpoint (registers the endpoint; runs no tests)
so a suite that needs startup state passes under --attach and fails under
--local against an empty scratch database, with nothing in the failure pointing
at the cause. The tests were asking for state the runner had prevented.
The run now names the displaced microflow and says --attach is the way to test
against an app that has actually started up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Re-running a `create or modify odata service` after editing a `publish entity` block did not apply the change. Marking a member Filterable and re-executing left the served $metadata exactly as it was; only `drop odata service` + create picked it up. The modify branch updated the service's scalar properties and never touched EntityTypes or EntitySets. Supplied entities now replace the stored ones wholesale. Replacing rather than merging is what makes the script the description of the service: a member removed from the script is removed from the service, which merging could not express. The same change carries AllowedModuleRoles across a modify. That is a guard, not a reproduction — the reported grant loss (mxcli-formula1 #26) did not reproduce on 11.12.1, on either the fixed or the previous build — but a modify cannot express grants, so it must not be able to drop them. Verified on 11.12.1: the same script yields `Label as 'label'` before and `Label as 'label' (Filterable, Sortable)` after, and the build stays at 0 errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
`create module role` had no `or modify` form, so re-running a security script failed on the first role that already existed and role creation had to live in its own run-once file. `create or modify module role` now updates an existing role's description instead of failing. AddModuleRole already overwrites, so it also adopts the caller's casing — the same path the auto-provisioned-role branch above it uses. `createModuleRoleStatement` carries its own CREATE keyword (it is dispatched from securityStatement, not from the shared createStatement rule), so the optional OR MODIFY goes in that rule and has to stay distinguishable from `create or modify module`. Both spellings are covered by a test, and the full doctype integration gate is green — the lesson from the last grammar change is that a `check`-only sweep proves nothing about what the visitor builds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
An attribute named `name` came out of CREATE EXTERNAL ENTITIES prefixed with the remote type — Stg_Drivername, Circuitname — so a page written against the published $metadata failed with "The selected attribute 'F1Live.Drivers.name' no longer exists", and the same field carried a different name in every module because the remote type names differ. `name` was simply not reserved. Adjudicated on 11.12.1 by importing a contract with a property for each name on the list and prefixing disabled: Mendix answered CE7247 "The name 'x' is a reserved word" for id, owner, changedBy, changedDate, createdDate, type and context — and said nothing about `name`. So this is one wrong entry, not a scheme that needs redesigning; the other seven earn their place and keep it. The remaining renames are now reported at the end of an import instead of being discovered later when a page will not build. Note for existing projects: a re-import renames `Stg_Drivername` back to `name`, which is the point, but anything referring to the old name has to follow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
MOVE accepted seven doctypes and rejected the rest at parse time (`no viable alternative at input 'MOVEJAVA'`). Neither CREATE JAVA ACTION nor CREATE ODATA SERVICE takes a folder clause either, so those documents could never leave the module root from MDL — five of the reporting project's documents were stuck there while the other 36 sorted into folders. Both are plain document units, so each reduces to the existing reparent primitive: the executor sets ContainerID and calls the backend, which persists the containment row and touches nothing else. sdk/mpr's moveUnitByID is exported for the doctypes that have no dedicated writer method of their own. Verified on 11.12.1: `move java action` and `move odata service` into 'Support' and 'Api/Published' created exactly three folders (two levels for the nested path), left the document count unchanged, and the project still loads and builds. Full doctype integration gate green — mandatory for a grammar change, and it caught a bad first draft of the new example. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…stics-spike-emta6h # Conflicts: # cmd/mxcli/cmd_test_run.go # cmd/mxcli/testrunner/runner.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Nine of the open findings in mxcli-formula1 FINDINGS.md, each verified against real mxbuild on Mendix 11.12.1.
Merged with
mainafter #113 landed. That PR fixed §15a (test --listpath resolution) and §19 (after-startup during--local) independently, and its §19 is the better answer — it actually chains the app's startup microflow and adds--skip-app-startup, where mine only said the microflow had been displaced. Both conflicts were resolved in favour ofmain, so this PR no longer claims either.Fixed
Integerwas written asEdm.Int32; Mendix publishes it asInt64, so every whole-number attribute in a service failed the buildcreate or modify external entitytouching only an entity-level property detonated every attribute (CE6612 each)namewas renamedStg_Drivername/Circuitname, so pages written against the contract would not build and the same field differed per modulenamecreate external entitiesignored the contract'sCountable/Filterable/Sortableannotations (CE6630)create odata clientfetched$metadatawithout the credentials on the statement, creating an empty client with only a warningexecute database query … dynamic $Sqlreached the runtime as the literal'$Sql', blocking runtime-built SQL outrightDynamicQuery\x00\x05\x00\x00\x00$Sql— five bytes, no quotescreate or modify odata serviceignored edits to apublish entityblockLabel as 'label'before andLabel as 'label' (Filterable, Sortable)after; build stays at 0 errorsMOVE JAVA ACTION/MOVE ODATA SERVICEwere parse errors, and neitherCREATEtakes a folder clause — so those documents could never leave the module rootUnitcount: three new folders (nested path creates two), document count unchanged, project still buildsCountable/SkipSupported/TopSupported, telling users accepted properties were unknowncreate module rolehad noor modify, so a security script could not be re-runThree findings turned out to be worth more than reported:
Edm.StringwithEnumerationAsStringhardcodedfalse, which is the one combination Mendix rejects — CE5016 plus CE4583. The type and the flag are one setting, so the flag now travels with the attribute.attributeFromGenhandledStoredValueandOqlViewValuebut notRest$ODataMappedValue, so every attribute read back unmapped and the next write emitted a plainStoredValue. This is the attribute-level half of External Entities : allow_create_change_locally does't work mendixlabs/mxcli#782.Two existing unit tests had pinned the wrong answers (
Edm.Int32, andname→AirlineName) and were corrected with the mxbuild evidence.One correction to the report
§26's role-grant loss did not reproduce. Grants survived a modify on 11.12.1 on both the fixed and the previous build. The carry-through is kept as a guard — a modify cannot express grants, so it should not be able to drop them — and the commit and code comment both say so rather than claiming a fix.
Not done
KEYon a persistable attribute with no unique validation. Needs the project, so it belongs in thecheck --referencespass.design-properties.json, socheckgreen-lights values the build rejects.SHOW STRUCTUREis flat,DESCRIBEomits it), so a move cannot be verified from mxcli..ai-context/skills/going stale after a binary upgrade. Needs a versioning or staleness-detection design.Migration note
Fixing §28 means a re-import renames
Stg_Drivernameback toname— which is the point, but anything referring to the prefixed name has to follow.Verification
Every fix was reverted and re-tested to confirm the new test fails with the reported symptom. Full unit suite green before and after the merge, and the doctype integration gate (all scripts, both engines, real
mx check) green — run after each of the two grammar changes, since acheck-only sweep proves nothing about what the visitor builds. It earned its keep: it caught a bad first draft of the new folder example thatmxcli checkpassed.Everything was verified on 11.12.1, not the 11.13.0 the findings were written against.
🤖 Generated with Claude Code
https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4